@fuzdev/fuz_app 0.143.0 → 0.145.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/auth/CLAUDE.md +8 -0
  2. package/dist/auth/cell_action_specs.d.ts +13 -0
  3. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  4. package/dist/auth/cell_action_specs.js +13 -0
  5. package/dist/auth/cell_actions.d.ts +7 -0
  6. package/dist/auth/cell_actions.d.ts.map +1 -1
  7. package/dist/auth/cell_actions.js +30 -0
  8. package/dist/auth/cell_ref_admission.d.ts +51 -0
  9. package/dist/auth/cell_ref_admission.d.ts.map +1 -0
  10. package/dist/auth/cell_ref_admission.js +69 -0
  11. package/dist/auth/deps.d.ts +6 -3
  12. package/dist/auth/deps.d.ts.map +1 -1
  13. package/dist/db/CLAUDE.md +40 -23
  14. package/dist/db/cell_ddl.d.ts +3 -2
  15. package/dist/db/cell_ddl.d.ts.map +1 -1
  16. package/dist/db/cell_ddl.js +3 -2
  17. package/dist/db/cell_queries.d.ts +31 -6
  18. package/dist/db/cell_queries.d.ts.map +1 -1
  19. package/dist/db/cell_queries.js +45 -18
  20. package/dist/db/fact_ddl.d.ts +30 -4
  21. package/dist/db/fact_ddl.d.ts.map +1 -1
  22. package/dist/db/fact_ddl.js +44 -4
  23. package/dist/db/fact_holder_queries.d.ts +70 -0
  24. package/dist/db/fact_holder_queries.d.ts.map +1 -0
  25. package/dist/db/fact_holder_queries.js +79 -0
  26. package/dist/db/fact_queries.d.ts +19 -14
  27. package/dist/db/fact_queries.d.ts.map +1 -1
  28. package/dist/db/fact_queries.js +31 -10
  29. package/dist/db/fact_store.d.ts +65 -9
  30. package/dist/db/fact_store.d.ts.map +1 -1
  31. package/dist/db/fact_store.js +49 -20
  32. package/dist/server/fact_security_headers.d.ts +54 -0
  33. package/dist/server/fact_security_headers.d.ts.map +1 -0
  34. package/dist/server/fact_security_headers.js +53 -0
  35. package/dist/server/serve_fact_route.d.ts +33 -7
  36. package/dist/server/serve_fact_route.d.ts.map +1 -1
  37. package/dist/server/serve_fact_route.js +36 -32
  38. package/dist/server/x_accel.d.ts +106 -30
  39. package/dist/server/x_accel.d.ts.map +1 -1
  40. package/dist/server/x_accel.js +533 -86
  41. package/dist/testing/CLAUDE.md +25 -1
  42. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  43. package/dist/testing/cross_backend/cell_crud.js +6 -3
  44. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  45. package/dist/testing/cross_backend/cell_gated_create.js +25 -16
  46. package/dist/testing/cross_backend/cell_ref_admission.d.ts +4 -0
  47. package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -0
  48. package/dist/testing/cross_backend/cell_ref_admission.js +268 -0
  49. package/dist/testing/cross_backend/expected_schema.json +66 -10
  50. package/dist/testing/cross_backend/fact_serving.d.ts +19 -1
  51. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  52. package/dist/testing/cross_backend/fact_serving.js +31 -9
  53. package/dist/testing/cross_backend/testing_reset_actions.d.ts +6 -2
  54. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  55. package/dist/testing/cross_backend/testing_reset_actions.js +13 -3
  56. package/package.json +3 -3
@@ -328,6 +328,14 @@ auth, ids)`: two queries for the whole id-set, the cells read through the
328
328
  owner — the author lands a 403, the anti-self-approval guard; a root owner
329
329
  reaches the check through the steward arm); approve flips
330
330
  `moderation → approved` + `visibility → public`, reject sets `rejected`.
331
+ - `auth/cell_ref_admission.ts` — `require_new_refs_held`, the
332
+ possession gate `cell_create` / `cell_update` / `cell_clone` run last before
333
+ their write: a non-admin write introducing a fact ref (beyond the cell's
334
+ current refs on update, each cloned row's source refs on clone) that its
335
+ acting actor holds no `fact_holder` row for is 403 `cell_ref_not_held`,
336
+ answered from holder rows alone (stored and unstored hashes read the same);
337
+ a schema without the fact chain refuses every new ref. Global admin bypasses;
338
+ a scoped admin doesn't. Twin of the Rust `cell_ref_admission`.
331
339
  - `auth/cell_grant_actions.ts` / `cell_field_actions.ts` /
332
340
  `cell_item_actions.ts` / `cell_audit_actions.ts` — the relation + ACL +
333
341
  audit handlers, exporting `to_grant_json` / `to_field_json` /
@@ -87,6 +87,19 @@ export declare const ERROR_CELL_CREATE_FORBIDDEN: "cell_create_forbidden";
87
87
  * anti-self-approval guard (twin of the Rust `ERROR_CELL_MODERATE_FORBIDDEN`).
88
88
  */
89
89
  export declare const ERROR_CELL_MODERATE_FORBIDDEN: "cell_moderate_forbidden";
90
+ /**
91
+ * Error reason — a non-admin write's `data` introduces a fact ref the acting
92
+ * actor does not hold. A **403** `forbidden`.
93
+ *
94
+ * A ref is new when the write's derived refs carry a hash its base doesn't —
95
+ * nothing on `cell_create`, the cell's current refs on `cell_update`, the
96
+ * source row's refs on each row `cell_clone` writes — and held when the acting
97
+ * actor has put the fact's bytes (a `fact_holder` row). The answer reads holder
98
+ * rows only, so it is the same for a fact that is stored and one that isn't.
99
+ * The gate is `auth/cell_ref_admission.ts` (twin of the Rust
100
+ * `ERROR_CELL_REF_NOT_HELD`).
101
+ */
102
+ export declare const ERROR_CELL_REF_NOT_HELD: "cell_ref_not_held";
90
103
  /**
91
104
  * Error reason — `cell_moderate` target has no governing root (`root_id` is
92
105
  * null): it's a root or an unparented cell, not a gated contribution, so there
@@ -1 +1 @@
1
- {"version":3,"file":"cell_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAEN,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAE3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAEN,0BAA0B,EAC1B,6BAA6B,EAC7B,2BAA2B,EAE3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAEN,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,EAE1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEN,2BAA2B,EAC3B,MAAM,8BAA8B,CAAC;AAGtC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc;;;EAAgC,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAO5D,OAAO,EACN,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,CAAC;AACF,OAAO,EAAE,0BAA0B,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,CAAC;AAClG,OAAO,EACN,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,EAC1B,CAAC;AACF,OAAO,EAAE,2BAA2B,EAAE,CAAC;AAIvC,uEAAuE;AACvE,eAAO,MAAM,oBAAoB,EAAG,gBAAyB,CAAC;AAE9D,yEAAyE;AACzE,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,EAAG,iBAA0B,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,EAAG,6BAAsC,CAAC;AAExF,6EAA6E;AAC7E,eAAO,MAAM,kCAAkC,EAAG,8BAAuC,CAAC;AAE1F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAG,iBAA0B,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,EAAG,uBAAgC,CAAC;AAE5E;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,EAAG,yBAAkC,CAAC;AAEhF;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,EAAG,yBAAkC,CAAC;AAEhF;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,EACpD,oCAA6C,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,EACrD,qCAA8C,CAAC;AAIhD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,oDAA0E,CAAC;AAChG,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;kBAkCnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAIhD;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;kBAsB1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;kBAAqC,CAAC;AACnE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAIhE;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;kBAQtB,CAAC;AACJ,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAQxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAI1D;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;kBAQ1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;kBAAqC,CAAC;AACnE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAIhE,eAAO,MAAM,eAAe;;;kBAG1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,gBAAgB;;;kBAG3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAIhE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;mBAsCb,CAAC;AACd,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAGzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAI5D;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;;;;;;;;kBAezB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;kBAAqC,CAAC;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAI9D;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;;;EAAmC,CAAC;AACvE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB;;;;;;;kBAM5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;kBAAqC,CAAC;AACrE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAIpE,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYC,CAAC;AAEtC,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYI,CAAC;AAEtC,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWC,CAAC;AAEtC,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;CAUC,CAAC;AAEtC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYG,CAAC;AAEtC,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYE,CAAC;AAEtC,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYD,CAAC;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYxB,CAAC"}
1
+ {"version":3,"file":"cell_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_action_specs.ts"],"names":[],"mappings":"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"}
@@ -92,6 +92,19 @@ export const ERROR_CELL_CREATE_FORBIDDEN = 'cell_create_forbidden';
92
92
  * anti-self-approval guard (twin of the Rust `ERROR_CELL_MODERATE_FORBIDDEN`).
93
93
  */
94
94
  export const ERROR_CELL_MODERATE_FORBIDDEN = 'cell_moderate_forbidden';
95
+ /**
96
+ * Error reason — a non-admin write's `data` introduces a fact ref the acting
97
+ * actor does not hold. A **403** `forbidden`.
98
+ *
99
+ * A ref is new when the write's derived refs carry a hash its base doesn't —
100
+ * nothing on `cell_create`, the cell's current refs on `cell_update`, the
101
+ * source row's refs on each row `cell_clone` writes — and held when the acting
102
+ * actor has put the fact's bytes (a `fact_holder` row). The answer reads holder
103
+ * rows only, so it is the same for a fact that is stored and one that isn't.
104
+ * The gate is `auth/cell_ref_admission.ts` (twin of the Rust
105
+ * `ERROR_CELL_REF_NOT_HELD`).
106
+ */
107
+ export const ERROR_CELL_REF_NOT_HELD = 'cell_ref_not_held';
95
108
  /**
96
109
  * Error reason — `cell_moderate` target has no governing root (`root_id` is
97
110
  * null): it's a root or an unparented cell, not a gated contribution, so there
@@ -27,6 +27,13 @@
27
27
  * silently truncate pages. The handler rejects the `created_by` filter
28
28
  * for null auth (account-id enumeration guard).
29
29
  *
30
+ * - `cell_create`, `cell_update`, and `cell_clone` gate the fact refs their
31
+ * `data` introduces on **possession**: a non-admin write may add a ref only
32
+ * to a fact its acting actor holds, else 403 `ERROR_CELL_REF_NOT_HELD` —
33
+ * answered from `fact_holder` rows alone, so it says nothing about whether a
34
+ * fact exists. Refs a cell (or a clone's source row) already carries stay
35
+ * admitted. The last gate before the write; see `auth/cell_ref_admission.ts`.
36
+ *
30
37
  * Mutations emit `cell_create` / `cell_update` / `cell_delete` audit
31
38
  * events via `deps.audit.emit(...)`. The `AuditLogConfig` threaded through
32
39
  * the consumer's `audit_factory` (see `create_app_backend`) must declare
@@ -1 +1 @@
1
- {"version":3,"file":"cell_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAIH,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAEN,KAAK,mBAAmB,EAExB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAoCN,KAAK,QAAQ,EAEb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EASN,KAAK,OAAO,EACZ,MAAM,uBAAuB,CAAC;AAkC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACxC,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,6HAA6H;IAC7H,IAAI,EAAE,QAAQ,CAAC;IACf,wHAAwH;IACxH,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,wJAAwJ;IACxJ,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,qbAAqb;IACrb,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,8GAA8G;IAC9G,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,mBAAmB,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjG;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG,CACjC,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,wBAAwB,KAC3B,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG;IAChD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC;IAC7C,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;CACvC,CAAC;AA6BF,eAAO,MAAM,YAAY,GAAI,KAAK,OAAO,KAAG,QAgB1C,CAAC;AAkDH,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB,GAAI,MAAM,cAAc,KAAG,KAAK,CAAC,SAAS,CA8pBzE,CAAC"}
1
+ {"version":3,"file":"cell_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAIH,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAEN,KAAK,mBAAmB,EAExB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAoCN,KAAK,QAAQ,EAEb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EASN,KAAK,OAAO,EACZ,MAAM,uBAAuB,CAAC;AAmC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACxC,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,6HAA6H;IAC7H,IAAI,EAAE,QAAQ,CAAC;IACf,wHAAwH;IACxH,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,wJAAwJ;IACxJ,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,qbAAqb;IACrb,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,8GAA8G;IAC9G,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,mBAAmB,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjG;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG,CACjC,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,wBAAwB,KAC3B,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG;IAChD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC;IAC7C,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;CACvC,CAAC;AA6BF,eAAO,MAAM,YAAY,GAAI,KAAK,OAAO,KAAG,QAgB1C,CAAC;AAkDH,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB,GAAI,MAAM,cAAc,KAAG,KAAK,CAAC,SAAS,CAorBzE,CAAC"}
@@ -27,6 +27,13 @@
27
27
  * silently truncate pages. The handler rejects the `created_by` filter
28
28
  * for null auth (account-id enumeration guard).
29
29
  *
30
+ * - `cell_create`, `cell_update`, and `cell_clone` gate the fact refs their
31
+ * `data` introduces on **possession**: a non-admin write may add a ref only
32
+ * to a fact its acting actor holds, else 403 `ERROR_CELL_REF_NOT_HELD` —
33
+ * answered from `fact_holder` rows alone, so it says nothing about whether a
34
+ * fact exists. Refs a cell (or a clone's source row) already carries stay
35
+ * admitted. The last gate before the write; see `auth/cell_ref_admission.ts`.
36
+ *
30
37
  * Mutations emit `cell_create` / `cell_update` / `cell_delete` audit
31
38
  * events via `deps.audit.emit(...)`. The `AuditLogConfig` threaded through
32
39
  * the consumer's `audit_factory` (see `create_app_backend`) must declare
@@ -53,6 +60,7 @@ import { can_view_cell, can_edit_cell, can_manage_cell, cell_viewer_from_auth }
53
60
  import { filter_visible_target_ids } from './cell_relation_visibility.js';
54
61
  import { CELL_VIEWER_UNRESTRICTED } from '../db/cell_view_sql.js';
55
62
  import { cell_steward_admits } from './cell_steward.js';
63
+ import { require_new_refs_held } from './cell_ref_admission.js';
56
64
  import { to_grant_json } from './cell_grant_actions.js';
57
65
  import { to_field_json } from './cell_field_actions.js';
58
66
  import { to_item_json } from './cell_item_actions.js';
@@ -254,6 +262,11 @@ export const create_cell_actions = (deps) => {
254
262
  }
255
263
  }
256
264
  }
265
+ // Possession-gated ref admission, the last gate before the write — every
266
+ // ref is new on a create. On the validated data — what the row stores and
267
+ // what `derive_cell_refs` writes into `cell.refs` — not the raw input
268
+ // `authorize_create` sees.
269
+ await require_new_refs_held(ctx, auth, null, validated_data);
257
270
  let row;
258
271
  try {
259
272
  row = await query_cell_create(ctx, {
@@ -387,6 +400,12 @@ export const create_cell_actions = (deps) => {
387
400
  if (input.data !== undefined)
388
401
  reject_kind_in_data(input.data);
389
402
  const validated_data = input.data !== undefined ? validate_data_or_throw(input.data) : undefined;
403
+ // Possession-gated ref admission against the cell's current refs: refs
404
+ // the cell already carries stay, whoever introduced them. A write with no
405
+ // `data` writes no refs and reads no holders.
406
+ if (validated_data !== undefined) {
407
+ await require_new_refs_held(ctx, auth, existing.refs, validated_data);
408
+ }
390
409
  let updated;
391
410
  try {
392
411
  updated = await query_cell_update(ctx, input.cell_id, {
@@ -495,6 +514,11 @@ export const create_cell_actions = (deps) => {
495
514
  * no container to be moderated under). So clone is no side door past the
496
515
  * creation policy: a caller denied a root `cell_create` of some kind can't
497
516
  * clone a viewable cell of that kind into one they own.
517
+ *
518
+ * Each row then passes ref admission against **its own source row's** refs
519
+ * (`require_new_refs_held`): what the source already carries is cloned
520
+ * without holding it, and only a ref the root's `patch` adds needs
521
+ * possession.
498
522
  */
499
523
  const clone_one_cell_row = async (ctx, source, auth, options) => {
500
524
  // A patch is wire-supplied `data` — reject a stray `kind` (kind is the
@@ -527,6 +551,12 @@ export const create_cell_actions = (deps) => {
527
551
  });
528
552
  }
529
553
  }
554
+ // Possession-gated ref admission against the source row's refs: a clone
555
+ // carries what the source already serves to this viewer; a patch adding a
556
+ // ref needs the caller to hold it. On the validated data — what the row
557
+ // stores and what `derive_cell_refs` writes into `cell.refs` — not the
558
+ // merged data `authorize_create` sees.
559
+ await require_new_refs_held(ctx, auth, source.refs, validated_data);
530
560
  return query_cell_create(ctx, {
531
561
  // Boundary cast (see `create_handler`).
532
562
  data: validated_data,
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Possession-gated cell ref admission — the gate `cell_create`, `cell_update`,
3
+ * and `cell_clone` run on the fact refs their `data` introduces.
4
+ *
5
+ * Fact serving admits a read of a hash through any cell the reader can view
6
+ * whose `refs` carry it, and refs derive from `data` alone
7
+ * (`derive_cell_refs`). Without this gate anyone who can write a cell could
8
+ * name any hash they had seen (in a URL, a header, an audit row) and serve its
9
+ * bytes through their own cell — to the world, if the cell is public — and
10
+ * read a fact's existence off the serve answer. So a write may introduce only
11
+ * refs to facts its acting actor **holds**: a `fact_holder` row, recorded on
12
+ * every put that names the actor, identical bytes put by a second actor
13
+ * included.
14
+ *
15
+ * Twin of the Rust `fuz_cell_actions` `cell_ref_admission` module.
16
+ *
17
+ * @module
18
+ */
19
+ import type { Json } from '@fuzdev/fuz_util/json.ts';
20
+ import type { QueryDeps } from '../db/query_deps.js';
21
+ import { type RequestActorContext } from './request_context.js';
22
+ /** The refusal message — byte-identical with the Rust spine's. */
23
+ export declare const CELL_REF_NOT_HELD_MESSAGE = "cell data references a fact the caller does not hold";
24
+ /**
25
+ * Refuse a write whose `data` introduces a fact ref the caller does not hold.
26
+ *
27
+ * `new_refs = derive_cell_refs(data) \ base`, where `base` is what the write
28
+ * already carries — `null` on `cell_create`, the cell's current refs on
29
+ * `cell_update`, the source row's refs on each row `cell_clone` writes — so
30
+ * keeping refs someone else introduced, or cloning a viewable cell's refs, is
31
+ * admitted (the bytes were already readable through that cell). A global
32
+ * admin is not gated; a scoped admin is. The check runs on the write's
33
+ * transaction, only when `new_refs` is non-empty — a table probe, then one
34
+ * holder lookup for every new ref at once — and answers from holder rows only:
35
+ * an unheld hash reads the same whether or not a fact is stored under it.
36
+ *
37
+ * A database without the fact chain (a consumer that mounts the cell verbs but
38
+ * no fact store) has no holders, so every new ref is refused there too —
39
+ * checked first, since naming a missing table would abort the transaction.
40
+ *
41
+ * Runs as the last gate before the row is written: after the 404 masks, the
42
+ * per-kind validator, and the create authorizer.
43
+ *
44
+ * @param deps - query deps on the write's transaction
45
+ * @param auth - the acting caller
46
+ * @param base - the refs the write already carries, `null` for none
47
+ * @param data - the `data` the write stores
48
+ * @throws ThrownJsonrpcError 403 `forbidden` with reason `cell_ref_not_held` when a new ref is unheld
49
+ */
50
+ export declare const require_new_refs_held: (deps: QueryDeps, auth: RequestActorContext, base: ReadonlyArray<string> | null, data: Json) => Promise<void>;
51
+ //# sourceMappingURL=cell_ref_admission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cell_ref_admission.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_ref_admission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAGrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAMrD,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAIjF,kEAAkE;AAClE,eAAO,MAAM,yBAAyB,yDAAyD,CAAC;AAEhG;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,qBAAqB,GACjC,MAAM,SAAS,EACf,MAAM,mBAAmB,EACzB,MAAM,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,EAClC,MAAM,IAAI,KACR,OAAO,CAAC,IAAI,CAcd,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Possession-gated cell ref admission — the gate `cell_create`, `cell_update`,
3
+ * and `cell_clone` run on the fact refs their `data` introduces.
4
+ *
5
+ * Fact serving admits a read of a hash through any cell the reader can view
6
+ * whose `refs` carry it, and refs derive from `data` alone
7
+ * (`derive_cell_refs`). Without this gate anyone who can write a cell could
8
+ * name any hash they had seen (in a URL, a header, an audit row) and serve its
9
+ * bytes through their own cell — to the world, if the cell is public — and
10
+ * read a fact's existence off the serve answer. So a write may introduce only
11
+ * refs to facts its acting actor **holds**: a `fact_holder` row, recorded on
12
+ * every put that names the actor, identical bytes put by a second actor
13
+ * included.
14
+ *
15
+ * Twin of the Rust `fuz_cell_actions` `cell_ref_admission` module.
16
+ *
17
+ * @module
18
+ */
19
+ import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
20
+ import { derive_cell_refs } from '../db/cell_queries.js';
21
+ import { query_fact_holder_table_exists, query_fact_holder_unheld } from '../db/fact_holder_queries.js';
22
+ import { has_scoped_role } from './request_context.js';
23
+ import { ROLE_ADMIN } from './role_schema.js';
24
+ import { ERROR_CELL_REF_NOT_HELD } from './cell_action_specs.js';
25
+ /** The refusal message — byte-identical with the Rust spine's. */
26
+ export const CELL_REF_NOT_HELD_MESSAGE = 'cell data references a fact the caller does not hold';
27
+ /**
28
+ * Refuse a write whose `data` introduces a fact ref the caller does not hold.
29
+ *
30
+ * `new_refs = derive_cell_refs(data) \ base`, where `base` is what the write
31
+ * already carries — `null` on `cell_create`, the cell's current refs on
32
+ * `cell_update`, the source row's refs on each row `cell_clone` writes — so
33
+ * keeping refs someone else introduced, or cloning a viewable cell's refs, is
34
+ * admitted (the bytes were already readable through that cell). A global
35
+ * admin is not gated; a scoped admin is. The check runs on the write's
36
+ * transaction, only when `new_refs` is non-empty — a table probe, then one
37
+ * holder lookup for every new ref at once — and answers from holder rows only:
38
+ * an unheld hash reads the same whether or not a fact is stored under it.
39
+ *
40
+ * A database without the fact chain (a consumer that mounts the cell verbs but
41
+ * no fact store) has no holders, so every new ref is refused there too —
42
+ * checked first, since naming a missing table would abort the transaction.
43
+ *
44
+ * Runs as the last gate before the row is written: after the 404 masks, the
45
+ * per-kind validator, and the create authorizer.
46
+ *
47
+ * @param deps - query deps on the write's transaction
48
+ * @param auth - the acting caller
49
+ * @param base - the refs the write already carries, `null` for none
50
+ * @param data - the `data` the write stores
51
+ * @throws ThrownJsonrpcError 403 `forbidden` with reason `cell_ref_not_held` when a new ref is unheld
52
+ */
53
+ export const require_new_refs_held = async (deps, auth, base, data) => {
54
+ if (has_scoped_role(auth, ROLE_ADMIN, null))
55
+ return;
56
+ const refs = derive_cell_refs(data);
57
+ if (refs === null)
58
+ return;
59
+ const new_refs = base === null ? refs : refs.filter((r) => !base.includes(r));
60
+ if (new_refs.length === 0)
61
+ return;
62
+ const all_held = (await query_fact_holder_table_exists(deps)) &&
63
+ (await query_fact_holder_unheld(deps, auth.actor.id, new_refs)).length === 0;
64
+ if (!all_held) {
65
+ throw jsonrpc_errors.forbidden(CELL_REF_NOT_HELD_MESSAGE, {
66
+ reason: ERROR_CELL_REF_NOT_HELD
67
+ });
68
+ }
69
+ };
@@ -8,7 +8,7 @@
8
8
  * @module
9
9
  */
10
10
  import type { Logger } from '@fuzdev/fuz_util/log.ts';
11
- import type { FactStore } from '@fuzdev/fuz_util/fact_store.ts';
11
+ import type { HeldFactStore } from '../db/fact_store.js';
12
12
  import type { Keyring } from './keyring.js';
13
13
  import type { PasswordHashDeps } from './password.js';
14
14
  import type { Db } from '../db/db.js';
@@ -63,9 +63,12 @@ export interface AppDeps {
63
63
  * serve binary content (facts) — minimal consumers leave it unset. The
64
64
  * consumer constructs a `PgFactStore` (`db/fact_store.ts`) over its facts
65
65
  * directory (`disk_root` + `fs`) at its own backend assembly and assigns it
66
- * here; `create_app_backend` stays facts-agnostic.
66
+ * here; `create_app_backend` stays facts-agnostic. Typed `HeldFactStore`, not
67
+ * the `@fuzdev/fuz_util` `FactStore`: its puts name a holder
68
+ * (`HeldFactPutOptions.holder`), the possession record cell ref admission
69
+ * reads.
67
70
  */
68
- fact_store?: FactStore;
71
+ fact_store?: HeldFactStore;
69
72
  }
70
73
  /**
71
74
  * Capabilities for route spec factories.
@@ -1 +1 @@
1
- {"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/deps.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAExF;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACvB;;;;;;;OAOG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACxD,qBAAqB;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,yBAAyB;IACzB,EAAE,EAAE,EAAE,CAAC;IACP,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,KAAK,EAAE,YAAY,CAAC;IACpB;;;;;;;;OAQG;IACH,iBAAiB,EAAE,cAAc,CAAC;IAClC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IACjC,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,KAAK,EAAE,YAAY,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,yBAA0B,SAAQ,iBAAiB;IACnE;;;;;;OAMG;IACH,iBAAiB,EAAE,gBAAgB,CAAC;CACpC"}
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/deps.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAExF;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACvB;;;;;;;OAOG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACxD,qBAAqB;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,yBAAyB;IACzB,EAAE,EAAE,EAAE,CAAC;IACP,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,KAAK,EAAE,YAAY,CAAC;IACpB;;;;;;;;OAQG;IACH,iBAAiB,EAAE,cAAc,CAAC;IAClC;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IACjC,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,KAAK,EAAE,YAAY,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,yBAA0B,SAAQ,iBAAiB;IACnE;;;;;;OAMG;IACH,iBAAiB,EAAE,gBAAgB,CAAC;CACpC"}
package/dist/db/CLAUDE.md CHANGED
@@ -71,8 +71,10 @@ the code.
71
71
 
72
72
  The universal mutable content primitive: a single `cell` table whose `data`
73
73
  JSONB is interpreted by view shape, with normalized relation + ACL sibling
74
- tables. `cell.refs` carries `blake3:` fact hashes auto-extracted from `data`
75
- on every write (cells-by-fact discovery). `path` is the **global** namespace
74
+ tables. `cell.refs` carries the fact hashes derived from `data` on every write
75
+ (`derive_cell_refs`: string values that are exactly a fact hash) — what fact
76
+ serving admits reads through, gated on write by possession
77
+ (`auth/cell_ref_admission.ts`). `path` is the **global** namespace
76
78
  axis — no tenant/hub scoping; globally unique on active rows. Soft delete via
77
79
  `deleted_at`; most indexes are partial on `deleted_at IS NULL`.
78
80
 
@@ -108,7 +110,8 @@ delete / set_moderation`, `_list_by_kind / _list_by_creator`, the
108
110
  run). `query_cell_set_moderation` is the dedicated
109
111
  `cell_moderate` write (`moderation` is absent from `CellUpdatePatch`, so an
110
112
  author can't self-approve via `cell_update`). `cell.refs` derived from
111
- `data` via `extract_refs` on create/update. `CellRow.grant_count` is a
113
+ `data` via the exported `derive_cell_refs` on create/update (exact
114
+ whole-string hashes, sorted — byte-identical with the Rust `extract_refs`). `CellRow.grant_count` is a
112
115
  derived projection (correlated subquery on `idx_cell_grant_cell`).
113
116
  - **`cell_moderation.ts`** — the `cell.moderation` read rules in pure and
114
117
  SQL form side by side (`cell_moderation_admits_audience` /
@@ -148,7 +151,7 @@ child_id`, fractional-index keyed): `query_cell_item_insert` (throws
148
151
  **Named projections** (rationale + helpers: ../../../docs/architecture.md
149
152
  §Query Modules). The consts on this side: `CELL_COLUMNS`,
150
153
  `CELL_GRANT_COLUMNS`, `CELL_FIELD_COLUMNS`, `CELL_ITEM_COLUMNS`, `FACT_COLUMNS`
151
- (the meta read derives `minus bytes`). The derived `grant_count` stays an
154
+ (the meta read derives `minus bytes`), `FACT_HOLDER_COLUMNS`. The derived `grant_count` stays an
152
155
  appended expression in the exported `cell_row_projection(alias)` rather than a
153
156
  `CELL_COLUMNS` entry, so the const is exactly the table's column set. Column
154
157
  order twins the Rust `fuz_cell` / `fuz_fact` `*_COLUMNS` consts. Every const is pinned to
@@ -159,21 +162,35 @@ exemption — by `src/test/db/column_projections.db.test.ts`; the cell
159
162
  ## Fact layer
160
163
 
161
164
  Content-addressed byte store. Cells reference facts by blake3 hash
162
- (`cell.refs`, auto-extracted from `data`); the fact layer stores the bytes.
163
- Optional — minimal consumers never migrate it.
165
+ (`cell.refs`, derived from `data`); the fact layer stores the bytes and
166
+ records who holds them. Optional — minimal consumers never migrate it.
164
167
 
165
168
  - **`fact_ddl.ts`** — `fact` (content-addressed bytes: embedded `bytes` xor
166
169
  `external_url`, CHECK enforces exactly one) + `fact_ref` (declared
167
170
  dependency edges; `target_hash` deliberately not an FK, for federation) +
168
- `memo` (`(fn_id, input_hash) → output_hash`). `FACT_MIGRATION_NS`, namespace
169
- `fuz_facts`.
171
+ `memo` (`(fn_id, input_hash) → output_hash`) + `fact_holder` (`(actor_id,
172
+ hash)` possession rows, both FKs cascading). `FACT_MIGRATION_NS`, namespace
173
+ `fuz_facts` — frozen and append-only (`full_fact_schema`, then
174
+ `fact_holder`), each entry statement-identical with the Rust `fuz_fact`
175
+ migration of the same name.
170
176
  - **`fact_queries.ts`** — mechanical `query_put_fact` (idempotent `ON CONFLICT
171
177
  DO NOTHING`), `_put_fact_refs`, `_get_fact` / `_get_fact_meta` / `_has_fact`
172
178
  / `_get_fact_refs`, `_delete_fact` (returns `{size, external_url}` for
173
179
  external unlink), and the cell-coupled orphan queries `query_orphan_facts_list`
174
- / `_select_for_delete` (a fact is orphan when no active `cell.refs` names it).
175
- - **`fact_store.ts`** — `PgFactStore implements FactStore` (the interface lives
176
- in `@fuzdev/fuz_util/fact_store.ts`): size-routed writes (embedded ≤
180
+ / `_select_for_delete` (a fact is orphan when no active `cell.refs` names it;
181
+ the `older_than` cutoff keeps a fact whose `created_at` or any
182
+ `fact_holder.created_at` is within the window, so a deduplicated re-upload
183
+ survives any sweep whose window covers it).
184
+ - **`fact_holder_queries.ts`** — `FACT_HOLDER_COLUMNS`, `query_put_fact_holder`
185
+ (idempotent upsert — a repeat put refreshes the row's `created_at` to the
186
+ most recent put), and the admission reads `query_fact_holder_table_exists` /
187
+ `query_fact_holder_unheld` (answered from holder rows alone).
188
+ - **`fact_store.ts`** — `PgFactStore`, implementing `HeldFactStore` (reads per
189
+ the `@fuzdev/fuz_util/fact_store.ts` `FactStore` contract; puts take
190
+ `HeldFactPutOptions`, whose required `holder` —
191
+ an actor id, or `null` for a system put — is recorded in `fact_holder` on
192
+ every put, a dedup hit included, in one transaction with the fact row and its
193
+ refs): size-routed writes (embedded ≤
177
194
  `embedded_threshold` / disk CAS above it), JSON ref auto-extract, idempotent
178
195
  put, verify-on-read for disk-backed content. With `disk_root` + `fs` (the
179
196
  `runtime/*Deps`) configured, oversize `put` and the streaming `put_stream`
@@ -199,27 +216,27 @@ DO NOTHING`), `_put_fact_refs`, `_get_fact` / `_get_fact_meta` / `_has_fact`
199
216
  - **`fact_store_errors.ts`** — `FactPayloadTooLargeError` / `FactStorageFullError` (+
200
217
  `is_enospc_error`) thrown by `put_stream`, for a consumer route's 413 / 507.
201
218
  - The HTTP serving plumbing lives under `server/` (`serve_fact_route.ts`,
202
- `x_accel.ts`).
219
+ `x_accel.ts`, `fact_security_headers.ts`).
203
220
 
204
221
  ### Migration namespace order
205
222
 
206
223
  FK dependency dictates order; consumers register via `migration_namespaces`
207
- on `create_app_backend` (after the built-in `fuz_auth` namespace). The fact
208
- namespace is FK-independent so its slot is free; the spine binary and every
209
- consumer place it last (cross-namespace order is immaterial to the
210
- `schema_version` rows — `sequence` is assigned per-namespace):
224
+ on `create_app_backend` (after the built-in `fuz_auth` namespace). Both
225
+ `fuz_cell` and `fuz_facts` FK into `actor`, so both follow `fuz_auth`; the
226
+ spine binary and every consumer place facts last (cross-namespace order is
227
+ otherwise immaterial to the `schema_version` rows — `sequence` is assigned
228
+ per-namespace):
211
229
 
212
230
  ```
213
231
  auth_migration_ns (built-in, fuz_auth)
214
232
  → CELL_MIGRATION_NS (fuz_cell — FKs actor.id)
215
- → FACT_MIGRATION_NS (fuz_facts — no external FK; slot is free)
233
+ → FACT_MIGRATION_NS (fuz_facts — fact_holder FKs actor.id)
216
234
  ```
217
235
 
218
236
  ### Pre-stable migration policy
219
237
 
220
- Scoped to the namespaces on this page (`fuz_cell` / `fuz_facts`): each is a
221
- single `full_*_schema` entry, so when it needs to change shape, **rewrite the
222
- original in place** rather than appending a
223
- follow-up — consumers re-bootstrap dev DBs on upgrade. The `fuz_auth`
224
- namespace is the opposite — frozen and append-only, since consumers hold
225
- deployed databases on it. See ../../../docs/migrations.md §Schema stability.
238
+ Scoped to `fuz_cell`: a single `full_cell_schema` entry, so when it needs to
239
+ change shape, **rewrite the original in place** rather than appending a
240
+ follow-up — consumers re-bootstrap dev DBs on upgrade. `fuz_facts` and
241
+ `fuz_auth` are the opposite — frozen and append-only, since consumers hold
242
+ deployed databases on them. See ../../../docs/migrations.md §Schema stability.
@@ -5,8 +5,9 @@
5
5
  * is interpreted by view shape. Parent→child membership and named
6
6
  * relationships live in two sibling tables — `cell_item` (ordered
7
7
  * children, fractional-indexing keyed) and `cell_field` (named edges).
8
- * `refs text[]` carries `blake3:` fact hashes auto-extracted from `data`
9
- * by application code on every write.
8
+ * `refs text[]` carries the fact hashes derived from `data` (string values
9
+ * that are exactly a fact hash, `derive_cell_refs`) by application code on
10
+ * every `data` write.
10
11
  *
11
12
  * Soft delete via `deleted_at`. Most indexes are partial on
12
13
  * `deleted_at IS NULL` so active-cell queries skip tombstones.
@@ -1 +1 @@
1
- {"version":3,"file":"cell_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,oIAI1B,CAAC;AAER;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,WAAW,gkBAgBtB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,MAAM,CAkBtC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,0iBAc5B,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAW5C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,+RAO5B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAE5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,qSAO3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,MAAM,CAG3C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,4DAA6D,CAAC;AAE3F,uBAAuB;AACvB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,SAAS,CAuB5C,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,EAAE,kBAG/B,CAAC"}
1
+ {"version":3,"file":"cell_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,oIAI1B,CAAC;AAER;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,WAAW,gkBAgBtB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,MAAM,CAkBtC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,0iBAc5B,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAW5C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,+RAO5B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAE5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,qSAO3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,MAAM,CAG3C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,4DAA6D,CAAC;AAE3F,uBAAuB;AACvB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,SAAS,CAuB5C,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,EAAE,kBAG/B,CAAC"}
@@ -5,8 +5,9 @@
5
5
  * is interpreted by view shape. Parent→child membership and named
6
6
  * relationships live in two sibling tables — `cell_item` (ordered
7
7
  * children, fractional-indexing keyed) and `cell_field` (named edges).
8
- * `refs text[]` carries `blake3:` fact hashes auto-extracted from `data`
9
- * by application code on every write.
8
+ * `refs text[]` carries the fact hashes derived from `data` (string values
9
+ * that are exactly a fact hash, `derive_cell_refs`) by application code on
10
+ * every `data` write.
10
11
  *
11
12
  * Soft delete via `deleted_at`. Most indexes are partial on
12
13
  * `deleted_at IS NULL` so active-cell queries skip tombstones.
@@ -4,11 +4,11 @@
4
4
  * Convention: `deps: QueryDeps` first, no audit side effects, mutations
5
5
  * return the affected row (or `null` for not-found).
6
6
  *
7
- * `cell.refs` is auto-extracted from `data` on every create and update via
8
- * `fact_hash_extract_refs` (depth-first walk for `blake3:`-prefixed strings). Callers
9
- * never pass `refs` directly — the column is a derived projection of
10
- * `data` for cells-by-fact discovery, mirroring what a fact store does for
11
- * JSON facts.
7
+ * `cell.refs` is derived from `data` on every create and update via
8
+ * `derive_cell_refs` (depth-first walk for string values that are exactly a
9
+ * fact hash). Callers never pass `refs` directly — the column is a derived
10
+ * projection of `data`, what fact serving admits a read through and what
11
+ * cell ref admission (`auth/cell_ref_admission.ts`) gates on a write.
12
12
  *
13
13
  * Soft delete via `deleted_at`. All `get` / `list` queries exclude
14
14
  * tombstones by default; `include_deleted: true` opts in for admin /
@@ -23,7 +23,7 @@
23
23
  import type { QueryDeps } from './query_deps.js';
24
24
  import type { Json } from '@fuzdev/fuz_util/json.ts';
25
25
  import type { Uuid } from '../primitive_schemas.js';
26
- import type { FactHash } from '@fuzdev/fuz_util/hash_schemas.ts';
26
+ import { type FactHash } from '@fuzdev/fuz_util/hash_schemas.ts';
27
27
  import type { CellData } from '../auth/cell_data_schema.js';
28
28
  import type { CellVisibility } from '../auth/cell_action_specs.js';
29
29
  /**
@@ -411,4 +411,29 @@ export interface CellListParams {
411
411
  /** Include soft-deleted rows. Default `false`. */
412
412
  include_deleted?: boolean;
413
413
  }
414
+ /**
415
+ * Derive the `refs` array column value from a cell's `data`: the string
416
+ * values that are exactly a fact hash, deduped and sorted.
417
+ *
418
+ * A ref is a **whole** string value matching the wire fact-hash grammar —
419
+ * `blake3:` followed by exactly sixty-four lowercase hex characters
420
+ * (`is_fact_hash`, `FACT_HASH_EXACT`). A hash inside prose or markup, an
421
+ * uppercase-hex digest, or a `blake3:` string of any other length is not a
422
+ * ref. Refs are what fact serving admits a read through and what cell ref
423
+ * admission gates on a write, so the grammar is exact: a string either is an
424
+ * address or isn't one. Object keys are not walked — only values. (JSON facts'
425
+ * `fact_ref` edges keep `fact_hash_extract_refs`, the substring scan.)
426
+ *
427
+ * Returns `null` (rather than `[]`) when no refs are present — the column
428
+ * is nullable and the `idx_cell_refs` partial index is `WHERE refs IS NOT
429
+ * NULL`, so an empty array would force every cell into the index.
430
+ *
431
+ * Sorts the deduped refs so the stored `text[]` is deterministic and
432
+ * byte-identical to the Rust twin (`fuz_cell`'s `extract_refs`); every ref is
433
+ * ASCII, so the code-unit sort here and the byte sort there agree.
434
+ *
435
+ * @param data - the cell's `data`
436
+ * @returns the sorted refs, or `null` when there are none
437
+ */
438
+ export declare const derive_cell_refs: (data: Json) => Array<FactHash> | null;
414
439
  //# sourceMappingURL=cell_queries.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cell_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAajE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,2KAegD,CAAC;AAK1E;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MAC2F,CAAC;AAEhJ;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,2DAKmC,CAAC;AAElE,iFAAiF;AACjF,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAE5E;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,SAAS,EACf,OAAO,oBAAoB,KACzB,OAAO,CAAC,OAAO,CAoBjB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAC1B,MAAM,SAAS,EACf,IAAI,IAAI,EACR,UAAU;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,KACrC,OAAO,CAAC,OAAO,GAAG,IAAI,CAUxB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,SAAS,EACf,MAAM,MAAM,KACV,OAAO,CAAC,OAAO,GAAG,IAAI,CAQxB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,KAAK,aAAa,CAAC,IAAI,CAAC,KACtB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAQxB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,SAAS,EACf,KAAK,aAAa,CAAC,IAAI,CAAC,KACtB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAQ5B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,SAAS,EACf,IAAI,IAAI,EACR,OAAO,eAAe,KACpB,OAAO,CAAC,OAAO,GAAG,IAAI,CA4BxB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,SAAS,EACf,IAAI,IAAI,EACR,UAAU;IAAE,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;CAAE,KACpC,OAAO,CAAC,OAAO,CAWjB,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,SAAS,EACf,IAAI,IAAI,EACR,YAAY,MAAM,EAClB,SAAS;IAAE,qBAAqB,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;CAAE,KACnE,OAAO,CAAC,OAAO,GAAG,IAAI,CAYxB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,SAAS,EACf,MAAM,MAAM,EACZ,UAAU,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,QAAQ,CAAC,KACjD,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CASvB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,SAAS,EACf,UAAU,IAAI,EACd,UAAU,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,QAAQ,CAAC,KACjD,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAQvB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,eAAO,MAAM,eAAe,GAC3B,MAAM,SAAS,EACf,QAAQ,cAAc,KACpB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CA4DxB,CAAC;AAiFF,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC9B,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,4EAA4E;IAC5E,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,0EAA0E;IAC1E,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iGAAiG;IACjG,OAAO,CAAC,EAAE,IAAI,CAAC;IACf;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAClB;;;;OAIG;IACH,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B;;;;OAIG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAChD;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACzD;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC;IACvC,sCAAsC;IACtC,eAAe,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACjC,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B"}
1
+ {"version":3,"file":"cell_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_queries.ts"],"names":[],"mappings":"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"}